/* This file is part of the KDE project
 * SPDX-FileCopyrightText: 2007 Jan Hambrecht <jaham@gmx.net>
 *
 * SPDX-License-Identifier: LGPL-2.0-or-later
 */

#ifndef KOENHANCEDPATHFORMULA_H
#define KOENHANCEDPATHFORMULA_H

#include <QString>
#include <QVariant>

class EnhancedPathShape;

class FormulaToken
{
public:
    /// token types
    enum Type {
        TypeUnknown = 0, ///< unknown type
        TypeNumber,      ///< 14, 3, 1977, 3.141592, 1e10, 5.9e-7
        TypeOperator,    ///< +, *, /, -
        TypeReference,   ///< function reference, modifier reference or named variable
        TypeFunction     ///< function name
    };

    /// operator types
    enum Operator {
        OperatorInvalid,   ///< invalid operator
        OperatorAdd,       ///< + addition
        OperatorSub,       ///< - subtraction
        OperatorMul,       ///< * multiplication
        OperatorDiv,       ///< / division
        OperatorLeftPar,   ///< (left parentheses
        OperatorRightPar,  ///<) right parentheses
        OperatorComma      ///< , comma
    };

    /// Constructs token with given type, text and position
    explicit FormulaToken(Type type = TypeUnknown, const QString &text = QString(), int position = -1);

    /// copy constructor
    FormulaToken(const FormulaToken &token);

    /// assignment operator
    FormulaToken &operator=(const FormulaToken &token);

    /// Returns the type of the token
    Type type() const
    {
        return m_type;
    }
    /// Returns the text representation of the token
    QString text() const
    {
        return m_text;
    }
    /// Returns the position of the token
    int position() const
    {
        return m_position;
    }

    /// Returns if the token is a number
    bool isNumber() const
    {
        return m_type == TypeNumber;
    }
    /// Returns if the token is a operator, OperatorInvalid if token is no operator
    bool isOperator() const
    {
        return m_type == TypeOperator;
    }
    /// Returns if token is a function
    bool isFunction() const
    {
        return m_type == TypeFunction;
    }
    /// Returns  if token is a reference
    bool isReference() const
    {
        return m_type == TypeReference;
    }

    /// Returns the token converted to qreal
    qreal asNumber() const;
    /// Returns the token as operator
    Operator asOperator() const;
private:
    Type m_type {TypeUnknown};    ///< the token type
    QString m_text; ///< the token text representation
    int m_position {-1}; ///< the tokens position
};

typedef QList<FormulaToken> TokenList;

class Opcode;

class EnhancedPathFormula
{
public:
    /// predefined functions
    enum Function {
        FunctionUnknown,
        // unary functions
        FunctionAbs,
        FunctionSqrt,
        FunctionSin,
        FunctionCos,
        FunctionTan,
        FunctionAtan,
        // binary functions
        FunctionAtan2,
        FunctionMin,
        FunctionMax,
        // ternary functions
        FunctionIf
    };

    /// The possible error code returned by error()
    enum Error {
        ErrorNone,    ///< no error
        ErrorValue,   ///< error when converting value
        ErrorParse,   ///< parsing error
        ErrorCompile, ///< compiling error
        ErrorName     ///< invalid function name value
    };

    /// Constructs a new formula from the specified string representation
    EnhancedPathFormula(const QString &text, EnhancedPathShape *parent);

    /// Destroys the formula
    ~EnhancedPathFormula();

    /**
     * Evaluates the formula using the given path as possible input.
     *
     * @param path the path to use as input
     * @return the evaluated result
     */
    qreal evaluate();

    /// Returns the last occurred error
    Error error() const
    {
        return m_error;
    }

    /// returns string representation of the formula
    QString toString() const;
private:
    /// Separates the given formula text into tokens.
    TokenList scan(const QString &formula) const;

    /// Compiles the formula tokens into byte code
    bool compile(const TokenList &tokens);

    /**
     * Evaluates a predefined function.
     *
     * @param function the identifier of the function to evaluate
     * @param arguments the functions arguments
     */
    qreal evaluateFunction(Function function, const QList<qreal> &arguments) const;

    /// Prints token list
    void debugTokens(const TokenList &tokens);
    /// Prints byte code
    void debugOpcodes();

    bool m_valid;    ///< flag that shows if function is valid, i.e the function was compiled successfully
    bool m_compiled; ///< flag that shows if function was compiled
    Error m_error;   ///< the last occurred error
    QString m_text; ///< the formula text representation
    QList<QVariant> m_constants; ///< constant values
    QList<Opcode> m_codes; ///< the compiled byte code
    EnhancedPathShape *m_parent;
};

#endif // KOENHANCEDPATHFORMULA_H
